<?php
require_once "lib/validation/ValidationException.php";
require_once 'lib/validation/ValidationInterface.php';

/**
 * Validation class.
 *
 * @author      Maciej Zawieja.
 * @link        http://akvus.pl
 * @package     Akvus Framework.
 * @subpackage  Libraries.
 * @version     1.0.3 - 19.08.2011 (Framework 1.3)
 */
class Validation implements iValidation {
    # consts
    const LESS_THAN = "LessThan";
    const GREATER_THAN = "GreaterThan";
    const BETWEEN = "Between";
    const REG_EX = "RegEx";
    const EMAIL = "Email";
    const TYPE = "Type";
        const TYPE_BOOL = 1;
        const TYPE_INT = 2;
        const TYPE_FLOAT = 4;
        const TYPE_DOUBLE = 4;
        const TYPE_STRING = 8;
        const TYPE_ARRAY = 16;
    const STRING_LENGTH = "StringLength";
    const IDENTICAL = "Identical";
    const ALPHA = "Alpha";
    const ALNUM = "AlNum";
    const NUMERIC = "Numeric";
    const IP = "Ip";
    const ISBN = "Isbn";
    const HOSTNAME = "Hostname";
    const NOT_EMPTY = "NotEmpty";

    /**
     * Array of rules.
     * @var array
     */
    private $rules;

    /**
     * Array of validation errors.
     * @var array
     */
    private $errors;

    /**
     * Determines if validation was run.
     * @var boolean
     */
    private $validationExecuted;

    /**
     * Constructor can add rules or even validate string while is creating 
     * its validation object instance. You just need to pass the array of rules
     * with params and the text to validate. Is there will be no exception throws
     * that means your string is valid.
     *
     * @throws ProgrammerException, ValidationException
     *
     * @param array $rules Optional rules to add while creating Validation obj.
     *                     It's better to use Validation::checkMulti() method insted.
     * @param string $text Optional string to validate. When passed validation is
     *                      taking place in the moment of creating validation obj.
     */
    function  __construct( array $rules = null, $text = null ) {
        $this->rules = $this->errors = array();
        $this->validationExecuted = false;

        if ( $rules != null ) $this->rules = $rules;
        if ( $text != null ) $this->isValid ($text);
    }

    /**
     * Add single rule.
     * @param flag $rule Flag represents rule.
     * @param array $params Rule params.
     * @return Validation
     */
    function addRule( $rule, $params )
    {
        $this->rules[$rule] = $params;
        return $this;
    }

    /**
     * Add rules.
     *
     * @param array $rules Rules to add array(Validation::RULE=>array($params,[...])).
     * @return Validation
     */
    function addRules( array $rules )
    {
        $this->rules = array_merge( $this->rules, $rules );
        return $this;
    }

    /**
     * Returns rules array.
     * 
     * @return array
     */
    function getRules()
    {
        return $this->rules;
    }

    /**
     * Drop rule given in the parameter.
     * @param flag $rule Rule to drop.
     * @return Validation
     */
    function dropRule( $rule )
    {
        if ( array_key_exists( $rule, $this->rules ) )
                unset ( $this->rules[$rule] );
        return $this;
    }

    /**
     * Drop rules provided in prameter array.
     *
     * @param array $param Rules to drop.
     * @return Validation
     */
    function dropRules( array $rules )
    {
        foreach ( $rules as $v ) {
            if ( array_key_exists( $v, $this->rules ) )
                    unset ($this->rules[$v]);
        }
        return $this;
    }

    /**
     * Get validation errors.
     * @return array
     */
    function getErrors()
    {
        return $this->errors;
    }

    /**
     * Determines if validation process was successfull.
     * @return boolean 
     */
    function success()
    {
        if ( $this->validationExecuted == false )
                throw new ProgrammerException ('You must validate first then check success', 0);
        if ( empty ($this->errors) ) return true;
        return false;
    }

    /**
     * Check if the given text is valid following all the rules.
     *
     * @throws ProgrammerException, ValidationException
     *
     * @param string $text Text to check.
     * @return boolean Is valid.
     */
    function isValid( $text )
    {
        if ( empty ( $this->rules ) )
                throw new ProgrammerException ('No rules passed to validation object.', 0);

        foreach( $this->rules as $k=>&$v)
        {
            try{
                if (file_exists('lib/validation/' . $k . '.php')) {
                    require_once 'lib/validation/' . $k . '.php';
                    $obj = new $k();
                    if ($v != null) $obj->setParams($v);
                    $obj->isValid( $text ); // throws an ValidationException if not
                } else throw new ProgrammerException ('The requested rule does not exists.', 0);
            } catch ( ValidationException $e ) {
                array_push( $this->errors, $e->getCode() );
            }
        }
        $this->validationExecuted = true;
        if ( empty ( $this->errors ) ) return true;  // return true if no errors found
        return false;
    }

    /**
     * Clears the rules array.
     * 
     * @return Validation
     */
    function clear()
    {
        $this->rules = $this->errors = array();
        $this->validationExecuted = false;
        return $this;
    }

    /**
     * Clear ruels.
     * 
     * @return Validation
     */
    function clearRules()
    {
        $this->rules = array();
        return $this;
    }

    /**
     * Returns specyfied validation object.
     * Manual way of validation single rule.
     *
     * @throws ProgrammerException
     *
     * @param flag $validationType  Flag specyfy the type of validation object.
     * @return object               Validation object
     */
    static function get( $validationType )
    {
        if (file_exists('lib/validation/' . $validationType . '.php')) {
            require_once 'lib/validation/' . $validationType . '.php';
            return new $validationType();
        } else throw new ProgrammerException ('The requested rule does not exists.', 0);
    }

    /**
     * Check single rule if is valid.
     * 
     * @throws ProgrammerExcaption, ValidationException
     *
     * @param flag $rule Validation rule falg.
     * @param array $params Validation rule params array.
     * @param string $text String to validate.
     * @return boolean Is valid.
     */
    static function check( $rule, $params, $text )
    {
        if (file_exists('lib/validation/' . $rule . '.php')) {
            require_once 'lib/validation/' . $rule . '.php';
            $val = new $rule();
            if( $params != null ) $val->setParams($params);
            return $val->isValid($text);
        } else throw new ProgrammerException ('The requested rule does not exists.', 0);
    }

    /**
     * Check multiple rules validation.
     *
     * @throws ProgrammerException, ValidationException
     * 
     * @param array $rules Array of rules (as keys) and params (as arrays in values).
     * @param string $text String to validate.
     * @return boolean Is valid.
     */
    static function checkMulti( array $rules, $text )
    {
        $errors = array();
        foreach ( $rules as $k=>&$v ) {
            if (file_exists('lib/validation/' . $k . '.php')) {
                require_once 'lib/validation/' . $k . '.php';
                $val = new $k();
                if ($v != null) $val->setParams($v);
                try { $val->isValid($text); }
                catch (ValidationException $e) { array_push( $errors, $e->getCode() ); }
            } else throw new ProgrammerException ('The requested rule does not exists.', 0);
        }
        if( empty ($errors) ) return true;
        else return $errors;
    }
}